﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Reflection;

namespace Inovout.Descriptor
{
    public class PropertyDescriptor : MemberDescriptor, IValueGetter
    {
        public PropertyInfo PropertyInfo { get; private set; }
        public PropertyDescriptor(PropertyInfo propertyInfo)
            : base(propertyInfo)
        {
            this.PropertyInfo = propertyInfo;
            this.Getter = GetterSetterFactory.CreatePropertyGetterWrapper(PropertyInfo);

        }
        public IGetValue Getter { get; private set; }
        public bool IsReadOnly
        {
            get { return !PropertyInfo.CanWrite; }
        }
        public Type PropertyType
        {
            get { return PropertyInfo.PropertyType; }
        }



        public bool TryConvertValueFrom(object value, out object result)
        {
            result = null;
            TypeConverter converter = System.ComponentModel.TypeDescriptor.GetConverter(this.PropertyType);
            try
            {
                result = converter.ConvertFrom(value);
                return true;
            }
            catch (Exception ex)
            {
                Debugger.Log(1, "warning", ex.Message);
                return false;
            }
        }
        public object ConvertValueFrom(object value)
        {
            object result;
            TryConvertValueFrom(value, out result);
            return result;
        }
        public void SetValue(object instance, object value)
        {
            PropertyInfo.SetValue(instance, value, null);
        }
        public static LambdaExpression BuildPropertyAccessorExpression(ParameterExpression typeExpression, string propertyName)
        {
            return Expression.Lambda(BuildPropertyExpression(typeExpression, propertyName), typeExpression);
        }
        public static Expression BuildPropertyExpression(ParameterExpression typeExpression, string propertyName)
        {
            string[] propertyNames = propertyName.Split('.');
            Expression propertyExpression = typeExpression;
            PropertyDescriptor propertyDescriptor = null;
            Type entityType = typeExpression.Type;
            foreach (string name in propertyNames)
            {
                TypeDescriptor typeDescriptor = TypeDescriptor.Get(entityType);
                propertyDescriptor = typeDescriptor.GetProperty(name);

                propertyExpression = Expression.Property(propertyExpression, propertyDescriptor.PropertyInfo);

                entityType = propertyDescriptor.PropertyType;
            }
            return propertyExpression;
        }
        public object GetValue(object instance)
        {
            return this.Getter.Get(instance);

            //  return PropertyInfo.GetValue(instance, null);
        }
        public interface IGetValue
        {
            object Get(object target);
        }
        public static class GetterSetterFactory
        {
            public static IGetValue CreatePropertyGetterWrapper(PropertyInfo propertyInfo)
            {
                if (propertyInfo == null)
                    throw new ArgumentNullException("propertyInfo");
                if (propertyInfo.CanRead == false)
                    throw new InvalidOperationException("属性不支持读操作。");

                MethodInfo mi = propertyInfo.GetGetMethod(true);

                if (mi.GetParameters().Length > 0)
                    throw new NotSupportedException("不支持构造索引器属性的委托。");

                Type instanceType = typeof(GetterWrapper<,>).MakeGenericType(propertyInfo.DeclaringType, propertyInfo.PropertyType);
                return (IGetValue)Activator.CreateInstance(instanceType, propertyInfo);
            }
        }
        public class GetterWrapper<TTarget, TValue> : IGetValue
        {
            private Func<TTarget, TValue> _getter;

            public GetterWrapper(PropertyInfo propertyInfo)
            {
                if (propertyInfo == null)
                    throw new ArgumentNullException("propertyInfo");

                if (propertyInfo.CanRead == false)
                    throw new InvalidOperationException("属性不支持读操作。");

                MethodInfo m = propertyInfo.GetGetMethod(true);
                _getter = (Func<TTarget, TValue>)Delegate.CreateDelegate(typeof(Func<TTarget, TValue>), null, m);
            }

            public TValue GetValue(TTarget target)
            {
                return _getter(target);
            }
            object IGetValue.Get(object target)
            {
                return _getter((TTarget)target);
            }
        }
    }
}
